using Engine.Graphics.VertexStructures;
using SharpDX;
using SharpDX.Direct3D11;

namespace Engine.Graphics.RenderableObjects
{
    public class SplineGraphicsObject : IRenderableObject
    {
        #region Private Fields

        private const float Width = 5.0f;
        private int color = System.Drawing.Color.Red.ToArgb();
        private bool disposed = true;
        private Engine engine;
        private Buffer indexBuffer;
        private BufferDescription indexBufferDescription;
        private short[] indices;
        private Vector3[] middleLine;
        private Buffer vertexBuffer;
        private VertexBufferBinding vertexBufferBinding;
        private BufferDescription vertexBufferDescription;
        private CustomVertex.PositionColored[] vertices;

        #endregion Private Fields

        #region Public Constructors

        public SplineGraphicsObject(Engine engine, float offset, Vector3[] middleLine, System.Drawing.Color color)
            : this(engine, offset, middleLine)
        {
            this.color = color.ToArgb();
            CreateVertices(this.middleLine);
            //CreateVertices(middleLine);
            UpdateVertexBuffer();
        }

        public SplineGraphicsObject(Engine engine, float offset, Vector3[] middleLine)
        {
            this.engine = engine;

            //this.engine.Grafic.SplineRightRenderableObjects.Add(this);
            CreateMiddleLineForLane(middleLine, offset, out this.middleLine);
            //this.middleLine = middleLine;
            this.indices = new short[(this.middleLine.Length - 1) * 3];
            CreateVertices(this.middleLine);
            //CreateVertices(middleLine);

            this.vertexBufferDescription = new BufferDescription()
            {
                BindFlags = BindFlags.VertexBuffer,
                CpuAccessFlags = 0,
                // OptionFlags = ResourceOptionFlags.BufferStructured,
                SizeInBytes = 0,
                Usage = ResourceUsage.Default,
                //StructureByteStride = CustomVertex.PositionColored.SizeInBytes()
            };

            indexBufferDescription = new BufferDescription()
            {
                BindFlags = BindFlags.IndexBuffer,
                CpuAccessFlags = 0,
                SizeInBytes = 0,
                Usage = ResourceUsage.Default,
                OptionFlags = ResourceOptionFlags.None
            };

            UpdateVertexBuffer();
        }

        #endregion Public Constructors

        #region Public Methods

        public void Dispose()
        {
            if (this.vertexBuffer != null)
                this.vertexBuffer.Dispose();
            if (this.indexBuffer != null)
                this.indexBuffer.Dispose();
            this.disposed = true;
            //this.engine.Grafic.RenderableObjects.Remove(this);
        }

        public void Render(int renderStep)
        {
            DeviceContext deviceContext = this.engine.Device.ImmediateContext;
            lock (deviceContext)
            {
                this.engine.Grafic.ShaderManager[0].SetShader(this.engine.Grafic.ShaderManager[0].ColorShader);

                deviceContext.InputAssembler.SetVertexBuffers(0, this.vertexBufferBinding);
                deviceContext.InputAssembler.SetIndexBuffer(this.indexBuffer, SharpDX.DXGI.Format.R16_UInt, 0);
                deviceContext.InputAssembler.PrimitiveTopology = SharpDX.Direct3D.PrimitiveTopology.TriangleList;

                deviceContext.DrawIndexed(this.indices.Length, 0, 0);
            }
        }

        #endregion Public Methods

        #region Private Methods

        private static void CreateMiddleLineForLane(Vector3[] middleLine, float offset, out Vector3[] middleLineForOffset)
        {
            Vector3 direction;
            middleLineForOffset = new Vector3[middleLine.Length];

            for (int i = 0; i < middleLine.Length - 1; i++)
            {
                direction = middleLine[i + 1] - middleLine[i];
                direction.Normalize();
                middleLineForOffset[i].X = middleLine[i].X - direction.Z * offset;
                middleLineForOffset[i].Y = middleLine[i].Y + 5.0f;
                middleLineForOffset[i].Z = middleLine[i].Z + direction.X * offset;
            }
            {
                direction = middleLine[middleLine.Length - 1] - middleLine[middleLine.Length - 2];
                direction.Normalize();
                middleLineForOffset[middleLine.Length - 1].X = middleLine[middleLine.Length - 1].X - direction.Z * offset;
                middleLineForOffset[middleLine.Length - 1].Y = middleLine[middleLine.Length - 1].Y + 5.0f;
                middleLineForOffset[middleLine.Length - 1].Z = middleLine[middleLine.Length - 1].Z + direction.X * offset;
            }
        }

        private void CreateVertices(Vector3[] middleLine)
        {
            // Create Vertices;
            this.vertices = new CustomVertex.PositionColored[(middleLine.Length - 1) * 3];
            for (int i = 0; i < middleLine.Length - 1; i++)
            {
                Vector3 direction = this.middleLine[i + 1] - this.middleLine[i];
                direction.Normalize();
                Vector3.Multiply(ref direction, Width, out direction);
                float tmp = direction.X;
                direction.X = -direction.Z;
                direction.Z = tmp;

                this.vertices[i * 3] = new CustomVertex.PositionColored(Vector3.Add(middleLine[i + 1], direction), new Color4(this.color));
                this.vertices[i * 3 + 1] = new CustomVertex.PositionColored(Vector3.Subtract(middleLine[i + 1], direction), new Color4(this.color));
                this.vertices[i * 3 + 2] = new CustomVertex.PositionColored(middleLine[i], new Color4(System.Drawing.Color.NavajoWhite.ToArgb()));

                this.indices[i * 3] = (short)(i * 3);
                this.indices[i * 3 + 1] = (short)(i * 3 + 1);
                this.indices[i * 3 + 2] = (short)(i * 3 + 2);
            }
        }

        private void UpdateVertexBuffer()
        {
            if (!this.disposed)
                Dispose();

            // http://stackoverflow.com/questions/12034956/dynamic-vertexbuffer-in-sharpdx?rq=1w2
            this.indexBufferDescription.SizeInBytes = this.indices.Length * sizeof(short);
            this.indexBuffer = Buffer.Create(this.engine.Device, this.indices, this.indexBufferDescription);
            this.vertexBufferDescription.SizeInBytes = CustomVertex.PositionColored.SizeInBytes() * this.vertices.Length;
            this.vertexBuffer = Buffer.Create(this.engine.Device, this.vertices, this.vertexBufferDescription);
            this.vertexBufferBinding = new VertexBufferBinding(this.vertexBuffer, CustomVertex.PositionColored.SizeInBytes(), 0);
            this.disposed = false;
        }

        #endregion Private Methods
    }
}